C++ 类别识别问题

发布于 2024-07-09 01:35:03 字数 467 浏览 6 评论 0原文

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

假设我有一个动物向量,我想遍历该数组并查看元素是狗还是猫?

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

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

I'll phrase this in the form of an example to make it more clear.

Say I have a vector of animals and I want to go through the array and see if the elements are either dogs or cats?

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

}

I hope that's sort of clear. I know about typeid, but I don't really have any Dog object to compare it to and I would like to avoid creating a Dog object if I can.

Is there a way to do this? Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

別甾虛僞 2024-07-16 01:35:03

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

无论如何,如果您真的想要这样做,那么您应该这样做(请注意,取消引用迭代器将为您提供 Animal* 。因此,如果您这样做 **it< /code> 您将得到一个 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 类型都应该是多态的。 这意味着它必须具有或继承至少一个虚函数。

As others has noted, you should neither use the typeid, nor the dynamic_cast operator to get the dynamic type of what your pointer points to. virtual functions were created to avoid this kind of nastiness.

Anyway here is what you do if you really want to do it (note that dereferencing an iterator will give you Animal* . So if you do **it you will get an 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
    }
}

Note you can apply the typeid operator to types itself too, as shown above. You don't need to create an object for this. Also note the typeid way doesn't work if you pass it a pointer like typeid(*it) . Using it like that will give you just typeid(Animal*) which isn't useful.

Similar, dynamic_cast can be used:

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

Note that in both cases, your Animal type should be polymorph. That means it must have or inherited at least one virtual function.

神妖 2024-07-16 01:35:03

只要向量包含 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
    }
}

但您应该意识到这通常不是最佳实践。 您应该尝试使用多态性,而不是反对它。 换句话说,尝试编写一个由 DogCat 重写的虚拟 Animal 函数,并让编译器自动调用正确的函数。

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

You can use dynamic_cast, as long as the vector contains Animal pointers.

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

but you should be aware that this is generally not the best practice. You should try to work with polymorphism, not against it. In other words, try to write a virtual Animal function that Dog and Cat override, and let the compiler automagically call the right one.

(Also, dynamic_cast is relatively slow, so too many of them will hinder performance; whereas a virtual function call is general just a single instruction.)

苏别ゝ 2024-07-16 01:35:03

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

笼统地说:如果您的动物是狗,则不要做某事; 让动物层次结构知道当其对象之一是狗时该怎么做! :)

Are you sure you want to do that? What you're going to do is the exact contrary of polymorphism, and polymorphism is the best thing in object-oriented programming.

Loosely speaking: Don't do something if you animal is a Dog; let the Animal hierarchy know what to do when one of its objects is a Dog! :)

心欲静而疯不止 2024-07-16 01:35:03

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

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

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

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

If you really need your application-level to identify Dogs vs non-Dogs, you should avoid using RTTI (dynamic_cast and typeid), and make that knowledge explicit in your class hierarchy.

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

There are some minor performance benefits, but the primary benefit is exposing the necessary behavior in your class interface to maintenance programmers. RTTI (being as limited as it is) should not be required in order for a class hierarchy to function.

Now, along with what other people have said, it's likely that the isDog() function could be refactored into something that doesn't require knowledge of the entire hierarchy up-front (such as needsPoopCleanup()). Like everyone else said, you're losing the benefits of polymorphism if your application logic conditionally executes based on object type anyhow.

寄风 2024-07-16 01:35:03

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

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

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

You can use the typeid operator to do this, e.g.

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

This can't catch if it's a derived class of Dog, though. You can use a dynamic_cast for that. However, any use of typeid or dynamic_cast is often indicative of a design flaw. Usually, you don't need to know what your derived types are, and there's probably a better way that involves polymorphism. It's hard to give the right advice without a real example, though.

断肠人 2024-07-16 01:35:03

使用虚函数:

正如其他人的回应所示,使用虚函数通常实际上就足够了,并且是“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;
}

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

using virtual functions:

As indicated by others responses, using virtual functions will often actually be sufficient enough, and is the "C++" way of thinking. Here is an example of using virtual functions:

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

If this will not be sufficient, then others have already posted answers which actually use conditional logic instead of polymerized logic.

走野 2024-07-16 01:35:03

接受的答案是正确的,但您应该知道还有另一种选项尚未提及。 您可以在 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 也可以工作,但字符串更好地说明了这一点)。

只是另一种选择。

The accepted answer is correct, but you should know that there is another option as well that hasn't been mentioned. You could have a virtual function in the Animal class called "type()" which could return an int or a string (or any type that is comparable).

So for example:

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

This way you could just do:

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

The type function could return anything (an int unique to each derived type would work too, but strings illustrate it better).

Simply another option.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文