【问题标题】:How to handle one subclass have a method, while another subclass that doesn't have it?如何处理一个子类有方法,而另一个子类没有方法?
【发布时间】:2014-01-12 09:33:22
【问题描述】:

我有一个关于接口的问题,比如说:

class IAnimal
{ ...
Public:
    virtual void makeSound() = 0;
};

class Cat : public IAnimal
{  ...
    void makeSound() { std::cout << "purr" << std::endl; }
};
class Dog : public IAnimal
{  ...
    void makeSound() { std::cout << "bark" << std::endl; }
};
class AnimalFactory
{
    std::shared_ptr<IAnimal> createAnimal(animalType type)
    {  
        std::shared_ptr<IAnimal> animal;
        switch(type)
        { case animal_type::cat: animal = std::shared_ptr<Cat>(); break; 
          case animal_type::dog: animal = std::shared_ptr<Dog>(); break; 
        … }
        return animal;
    }
};

class App
{ ...
    std::shared_ptr<IAnimal> _animal;
    AnimalFactory::animal_type type;
    void haveCat()
    { ...
        type = AnimalFactory::animal_type::cat;
        _animal = AnimalFactory.createAnimal(type);
        _animal->makeSound();
        ...
    } 
};

现在,我需要这只猫来抓老鼠 无效的catchMouse() { std::cout

void haveCat()
{ ...
  type = AnimalFactory::animal_type::cat;
  _animal = AnimalFactory.createAnimal(type);
  _animal->makeSound();
  // catchMouse();
  ...
}

有几种可能的解决方案,但都不是很好。

  1. 在 IAnimal 中添加一个方法,然后在使用 AnimalFactory 创建猫之后,我可以从 IAnimal 调用 catchMouse() 方法。 但是 catchMouse 并不适用于所有动物,狗不要 catchMouse。向 IAnimal 中添加方法会污染界面,闻代码。
  2. 在 Cat 中添加一个公共方法 catchMouse(),并在 haveCat() 方法中将 _animal 强制转换为 Cat。

    {
    _cat = std::dynamic_pointer_cast<Cat>(AnimalFactory.createAnimal(type));
    _cat->makeSound();
    _cat->catchMouse();
    }
    

    但是有一个动态演员表,不好,对吧?

  3. 让Cat实现IAnimal接口,还有一个关于Mouse的接口,但是AnimalFactory只返回std::shared_ptr, 而且我们不能在 IAnimal 中调用 catchMouse。

我在这里的意思是,一个子类中有一个公共方法,而另一个子类没有,如果我们使用工厂,如何设计它。 请不要回复,让狗抓兔子,然后在IAnimal中添加catch()方法,这样,猫抓老鼠,狗抓兔子。

这个问题有什么好的解决方案?谢谢。

【问题讨论】:

  • 为什么要这么具体? find_foodcollect_foodconsume_food 之类的就足够了。
  • makeSound 看起来应该是一个虚函数。
  • 你已经用#2 回答了你自己的问题。当你需要一只猫时,你必须施放它。
  • 这听起来像是在滥用shared_ptr
  • 如果您要根据类型进行显式分支,不妨扔掉所有面向对象的废话,并像真正的男人在编写真正的程序时那样说switch (animal-&gt;type) { case CAT: ...。跨度>

标签: c++ inheritance interface subclass factory


【解决方案1】:

我认为您实际上不能从 shared_ptr 转换为 Cat,对吗?因此,在不了解您的代码库的情况下,我编写了一个小型测试程序,

#include <memory>
#include <string>
#include <iostream>

class IAnimal
{ 
public:
   virtual void makeSound(){}
};

class Cat : public IAnimal
{  
public:
    virtual void makeSound() { std::cout << "purr" << std::endl; }
    void catchMice(void){
        std::cout<<"mouse catched\n";
    }
};
class Dog : public IAnimal
{  
    virtual void makeSound() { std::cout << "bark" << std::endl; }
};

int main(int argc,char** argv){
    Cat* c = new Cat;
    std::shared_ptr<IAnimal> s(c);
    //you've basically got something of this sort don't you?
    s->makeSound();
    (static_cast<Cat*>(s.get()))->catchMice();
    return 0;
}

那应该做你想做的事。我认为您可能希望将 makeSound(void) 作为虚拟函数。

【讨论】:

  • 请关注问题,可以使用 smart_pointer.get() 进行强制转换,甚至不使用智能指针。或使用 dynamic_pointer_cast;无论如何,谢谢。
  • 没有回答你的问题吗?您想通过基类指针访问仅子类的函数,不是吗?您将其转换为子类指针,编译器将在编译时生成对该函数的调用。
  • 对不起,我看到你的回答了,你的意思是使用 cast 将 IAnimal 转换为 Cat?像解决方案#2。谢谢。
  • 好吧,减去动态转换。由于只有 Cats 可以捕捉老鼠,因此您在编译时就知道要转换的内容,因此使用 static_cast 将避免 dynamic_cast 引入的开销,这是您主要关心的问题。甚至像 Classic C 中这样的转换: (Cat*)(s->get()) 在这种情况下也可以正常工作。
【解决方案2】:

我想,解决问题的方法是解决方案 2 的变体:使用特殊方法 catch 制作特定接口 ICatcher 并执行以下操作:

AnimalFactory::animal_type type = AnimalFactory::animal_type::cat;
std::shared_ptr<IAnimal> animal = myAnimalFactory.createAnimal(type);
(dynamic_cast<ICatcher*>(animal.get()))->catch();

但在这里您应该确保 dynamic_cast 会成功或为此提供额外的检查。

也许,您还会对COM 中的超级接口IUnknown 及其QueryInterface 方法如何解决这个问题感兴趣。可以为该架构实现本地版本。

【讨论】:

    猜你喜欢
    • 2019-07-25
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多