2008-12-02 88 views
2

我將以示例的形式對此進行描述,以使其更加清晰。C++類標識問題

說我有一個動物矢量,我想通過數組來看看元素是否是狗或貓?

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

你不能這樣做,它需要是一個矢量或動物*,而不是動物。 – 2008-12-02 04:48:21

回答

6

正如其他人所指出的,您不應該使用typeiddynamic_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. 
    } 
} 

注意,在這兩種情況下,你的動物類型應多晶型。這意味着它必須具有或繼承至少一個虛擬功能。

+0

非常感謝,非常豐富。我的程序中有一些動態投射其他地方,但我只是把它變成了虛擬功能。它在一個評估碰撞檢測的區域,我無法承受性能影響。我仍然會在你的例子使用typeid的作爲,因爲它的恆定時間 – Chad 2008-12-02 05:03:35

2

只要向量包含動物指針,就可以使用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函數DogCat重寫,並讓編譯器自動調用正確的函數。

(另外,dynamic_cast是比較慢的,所以太多了會阻礙性能;而虛函數調用一般只是一個單一的指令。)

+0

的動態轉換的一個潛在的性能優勢在於,它允許「做什麼」,使可內聯非虛調用。這不是說vcall本身很慢,而是它不會被內聯。但是,你必須測量一下,以確定在特定情況下哪個更快。 – 2008-12-02 13:58:42

1

你確定要這麼做?你要做的是與多態的確切相反,多態是在面向對象編程中最好的東西。

鬆散地說:不要做點什麼如果你的動物是狗;讓Animal層次結構知道當它的一個對象是狗時該怎麼做! :)

0

您可以使用typeid運算符來執行此操作,例如,

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

這不能趕上如果它是一個派生類的Dog,雖然。你可以使用dynamic_cast。然而,typeiddynamic_cast的任何使用常常表明設計缺陷。通常,您不需要知道派生類型是什麼,並且可能有更好的方法涉及多態。雖然沒有一個真實的例子很難給出正確的建議。

1

如果您確實需要您的應用程序級別來識別狗與非狗,則應避免使用RTTI(dynamic_casttypeid),並在您的類層次結構中明確說明該知識。

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

有一些小的性能優勢,但主要的好處是公開必要的行爲,在你的類接口的維護程序員。爲了使類層次結構正常運行,不應要求RTTI(儘可能受限制)。

現在,連同其他人所說的,很可能將函數重構爲不需要事先知道整個等級的東西(例如needsPoopCleanup())。就像其他人所說的那樣,如果您的應用程序邏輯有條件地基於對象類型執行,您將失去多態的好處。

+0

的基類不應該需要知道它的派生類的層次起作用。我完全同意,needsCleanup是更好的,如果這是可能的,但isDog,動物isCat方法只是一個自釀,RTTI的劣質版本。如果你不能做得更好,你可以使用真實的IMO。 – 2008-12-02 14:12:25

+0

從編譯器支持的RTTI冗餘的角度來看,它只是次要的。如果需要區分「業務邏輯」級別,爲維護程序員提供一種「可執行文檔」(而不是讓他們「搞清楚」)稍微有用。 – Tom 2008-12-03 01:23:57

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

如果這是不夠的,然後其他人已經發布其實際使用的,而不是聚合邏輯條件邏輯的答案。

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") { } 

類型函數可以返回任何東西(一個int唯一的每個派生類型將工作太,但字符串說明它更好)。

只是另一種選擇。