我会以示例的形式对此进行说明,以使其更加清晰。
说我有一个动物矢量,我想通过阵列看看这些元素是狗还是猫?
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对象。
有办法做到这一点吗?提前谢谢。
答案 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
}
}
但你应该知道这通常不是最好的做法。你应该尝试使用多态,而不是反对它。换句话说,尝试编写Animal
和Dog
覆盖的虚拟Cat
函数,让编译器自动调用正确的函数。
(另外,dynamic_cast
相对较慢,因此太多会阻碍性能;而虚函数调用通常只是一条指令。)
答案 2 :(得分:1)
你确定要这样做吗?你要做的是与多态性完全相反,多态性是面向对象编程中最好的。
松散地说:如果动物是狗,不要做某事;让Animal层次结构知道当其中一个对象是Dog时该怎么做! :)
答案 3 :(得分:1)
如果您确实需要应用程序级别来识别Dogs与非Dogs,则应避免使用RTTI(dynamic_cast
和typeid
),并在类层次结构中明确说明这些知识。
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
。但是,typeid
或dynamic_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也可以,但是字符串更好地说明了它。)
只是另一种选择。