C ++类识别问题

时间:2008-12-02 04:08:58

标签: c++ class identification

我会以示例的形式对此进行说明,以使其更加清晰。

说我有一个动物矢量,我想通过阵列看看这些元素是狗还是猫?

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对象。

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

7 个答案:

答案 0 :(得分: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(*it)的指针,则typeid方式不起作用。像这样使用它只会给你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类型应该是多态的。这意味着它必须拥有或继承至少一个虚函数。

答案 1 :(得分:2)

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

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
    }
}

但你应该知道这通常不是最好的做法。你应该尝试使用多态,而不是反对它。换句话说,尝试编写AnimalDog覆盖的虚拟Cat函数,让编译器自动调用正确的函数。

(另外,dynamic_cast相对较慢,因此太多会阻碍性能;而虚函数调用通常只是一条指令。)

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

如果您确实需要应用程序级别来识别Dogs与非Dogs,则应避免使用RTTI(dynamic_casttypeid),并在类层次结构中明确说明这些知识。

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

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

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分: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;
}

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

答案 6 :(得分: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也可以,但是字符串更好地说明了它。)

只是另一种选择。