首页 > 解决方案 > 如何在面向对象语言中打开子类型?

问题描述

我主要来自函数式语言背景,我不知道如何在面向对象的语言中很好地完成这种模式。

假设我有一些 Animal 类和子类以及一个 AnimalManager。我想要一个 AnimalManager 上的“add”方法,它具有不同的行为,具体取决于我添加的是猫还是狗。像这样的东西(没有特定的语言):

class Animal
class Cat : Animal
class Dog : Animal

class AnimalManager {
  void add(Animal a) { 
    a match {
       case Cat => // do something with cats
       case Dog => // do different something with dogs
    } 
  }
}

现在,如果我使用像 C++ 这样不支持以这种方式匹配类型的语言怎么办。我能想到的两个解决方案是:

  1. 使用 dynamic_cast。但是,有人告诉我应该避免动态转换,因为它会导致运行时错误,所以这可能是不好的做法。
  2. 使用虚函数。我可以做类似以下的事情(同样没有特定的语言,但你应该明白要点):
class Animal {
  virtual void beAdded(AnimalManager am)
}
class Cat : Animal {
  void beAdded(AnimalManager am) {
    am.doThingsWithCats(this)
  }  
}
class Dog : Animal {
  void beAdded(AnimalManager am) {
    am.doThingsWithDogs(this)
  }  
}

class AnimalManager {
  void doThingsWithDogs(Dog d) {
    // do something with dogs
  }
  void doThingWithCats(Cat d) {
    // do something with cats
  }
  void add(Animal a) {
    a.beAdded(this)
  } 
}

这显然非常尴尬并且还有其他问题(现在 Animal 需要了解 AnimalManager?)。

在面向对象语言中执行这种模式的最佳方法是什么?我应该知道这里有一些“设计模式”吗?

标签: c++oopdesign-patternstypes

解决方案


我建议您提供真实用例而不是隐喻,因为您可能遇到XY 问题。您可以在这里使用 RTTI,还有一个我想详细说明的问题。

不要使用void add(Animal a)which将您的对象切片为 just Animal,您将始终得到Animal而不是派生类CatDog. 用于void add(Aniaml& a)保留对象或使用(智能)指针。

#include <iostream>

class Animal{
public:
// Read Item 7 in Scott Meyers' Effective C++ if you don't know why you need it
#include <iostream>

class Animal{
public:
    virtual ~Animal(){};
};
class Cat : public Animal{

};
class Dog : public Animal{

};

class AnimalManager {
public:
#define REF < comment it to see what your code do
#ifdef REF
  void add(Animal& a) { 
#else
  void add(Animal a) { 
#endif
    using std::cout;
    using std::endl;
    if(typeid(a) == typeid(Cat)){
        cout << "Meow" << endl;
    }else if(typeid(a) == typeid(Dog)){
        cout << "Bark" << endl;
    }else{
        cout << "I'm just animal" << endl;
    }
  }
};
int main()
{
    Dog d;
    Cat a;
    AnimalManager m;
    m.add(d);
    m.add(a);
}

现场演示

供参考

Bark
Meow

按值传递

"I'm just animal
"I'm just animal

推荐阅读