2

我将用一个例子的形式来表达这一点,以使其更清楚。

假设我有一个动物向量,我想通过数组查看元素是狗还是猫?

class Dog: public Animal{/*...*/};
class Cat: public Animal{/*...*/};

int main()
{
vector<Animal*> stuff;
//cramming the dogs and cats in...

for(/*all elements in stuff*/)
//Something to the effect of:  if(stuff[i].getClass()==Dog) {/*do something*/}

}

我希望这有点清楚。我知道 typeid,但我真的没有任何 Dog 对象可以与之比较,如果可以的话,我想避免创建 Dog 对象。

有没有办法做到这一点?提前致谢。

4

7 回答 7

6

正如其他人所指出的,您既不应该使用typeid, 也不应该使用dynamic_cast运算符来获取指针指向的动态类型。创建虚函数是为了避免这种讨厌的事情。

无论如何,如果您真的想这样做,这就是您要做的(请注意,取消引用迭代器会给您Animal*。因此,如果您这样做**it,您将得到一个Animal&):

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(typeid(**it) == typeid(Dog)) {
        // it's a dog
    } else if(typeid(**it) == typeid(Cat)) {
        // it's a cat
    }
}

请注意,您也可以将typeid运算符应用于类型本身,如上所示。您不需要为此创建对象。另请注意,如果您将指针传递给 typeid 方式,则它不起作用typeid(*it)。像那样使用它只会给你带来typeid(Animal*)无用的东西。

类似,dynamic_cast可以使用:

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(Dog * dog = dynamic_cast<Dog*>(*it)) {
        // it's a dog (or inherited from it). use the pointer
    } else if(Cat * cat = dynamic_cast<Cat*>(*it)) {
        // it's a cat (or inherited from it). use the pointer. 
    }
}

请注意,在这两种情况下,您的 Animal 类型都应该是多态的。这意味着它必须具有或继承至少一个虚拟功能。

于 2008-12-02T04:40:06.523 回答
2

dynamic_cast只要向量包含 Animal 指针,您就可以使用。

vector <Animal *> stuff;

for(int i=0;i<stuff.size();i++) {
    Dog *pDog = dynamic_cast <Dog *> (stuff[i]);
    if(pDog) {
        // do whatever with the dog
    }

    Cat *pCat = dynamic_cast <Cat *> (stuff[i]);
    if(pCat) {
        // and so on
    }
}

但您应该知道,这通常不是最佳做法。您应该尝试使用多态性,而不是反对它。换句话说,尝试编写一个虚Animal函数DogCat覆盖,并让编译器自动调用正确的函数。

(而且,dynamic_cast速度相对较慢,所以太多会影响性能;而虚函数调用通常只是一条指令。)

于 2008-12-02T04:13:09.683 回答
1

你确定要这样做吗?你要做的是与多态完全相反,多态是面向对象编程中最好的东西。

粗略地说:如果您的动物是狗,请不要做某事;让 Animal 层次结构知道当其中一个对象是 Dog 时该做什么!:)

于 2008-12-02T04:14:49.460 回答
1

如果你真的需要你的应用程序级别来识别狗和非狗,你应该避免使用 RTTI (dynamic_casttypeid),并在你的类层次结构中明确表示这些知识。

for (size_t i = 0; i != v.size(); ++i) {
    if (v[i]->isDog()) { v->cleanupPoop(); }
}

有一些小的性能优势,但主要的优势是将类接口中的必要行为公开给维护程序员。为了使类层次结构起作用,不应该需要 RTTI(尽可能受限)。

现在,与其他人所说的一样,该isDog()函数很可能被重构为不需要预先了解整个层次结构的东西(例如needsPoopCleanup())。就像其他人所说的那样,如果您的应用程序逻辑无论如何基于对象类型有条件地执行,那么您将失去多态性的好处。

于 2008-12-02T04:59:04.927 回答
0

您可以使用typeid运算符来执行此操作,例如

if (typeid(stuff[i].getClass())==typeid(Dog))

但是,如果它是 的派生类,则无法捕获Dog。你可以使用 a dynamic_cast。然而,任何使用typeiddynamic_cast通常表示设计缺陷。通常,您不需要知道派生类型是什么,并且可能有更好的方法涉及多态性。但是,如果没有真实的例子,很难给出正确的建议。

于 2008-12-02T04:16:18.523 回答
0

使用虚函数:

正如其他人的反应所示,使用虚函数实际上就足够了,并且是“C++”的思维方式。下面是一个使用虚函数的例子:

#include<iostream>
#include<vector>
using namespace std;

/////////////

class Animal {
  public:
    virtual void move() { cout << "animal just moved" << endl; }
};
class Dog : public Animal {
  public:
    void move() { cout << "dog just moved" << endl; }
};
class Cat : public Animal {
  public:
    void move() { cout << "cat just moved" << endl; }
};

void doSomethingWithAnimal(Animal *a) {
  a->move();
}

/////////////

int main() {
  vector<Animal*> vec;
  vector<Animal*>::iterator it;

  Animal *a = new Animal;
  Dog *d = new Dog;
  Cat *c = new Cat;

  vec.push_back(a);
  vec.push_back(d);
  vec.push_back(c);

  it = vec.begin();

  while( it != vec.end() ) {
    doSomethingWithAnimal(*it);

    it++;
  }

  return 0;
}

如果这还不够,那么其他人已经发布了实际上使用条件逻辑而不是聚合逻辑的答案。

于 2008-12-02T05:14:23.460 回答
0

接受的答案是正确的,但您应该知道还有另一个选项没有被提及。你可以在 Animal 类中有一个名为“type()”的虚函数,它可以返回一个 int 或一个字符串(或任何可比较的类型)。

例如:

class Animal {
    /*...*/
public:
    virtual std::string type() const { return "animal"; }
};

class Dog: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "dog"; }
};

class Cat: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "cat"; }
};

这样你就可以这样做:

if(array[i]->type() == "dog") { }

type 函数可以返回任何东西(每个派生类型唯一的 int 也可以,但字符串可以更好地说明它)。

只是另一种选择。

于 2008-12-02T05:15:49.573 回答