Vâng, hồ sơ không bao giờ nói dối.
Vì tôi có một hệ thống phân cấp khá ổn định gồm 18-20 loại không thay đổi nhiều, tôi tự hỏi liệu chỉ cần sử dụng một thành viên enum đơn giản sẽ làm được điều đó và tránh được chi phí RTTI "cao". Tôi đã hoài nghi nếu RTTI thực tế đắt hơn chỉ là if
tuyên bố mà nó giới thiệu. Chàng trai ơi, là nó.
Nó chỉ ra rằng RTTI là đắt tiền, nhiều hơn nữa đắt hơn tương đương if
tuyên bố hoặc một đơn giản switch
trên một biến nguyên thủy trong C ++. Vì vậy, câu trả lời S. Lott là không hoàn toàn chính xác, có là thêm chi phí cho RTTI, và nó không do chỉ có một if
tuyên bố trong hỗn hợp. Đó là do RTTI rất đắt tiền.
Thử nghiệm này được thực hiện trên trình biên dịch Apple LLVM 5.0, với tối ưu hóa kho được bật (cài đặt chế độ phát hành mặc định).
Vì vậy, tôi có dưới 2 chức năng, mỗi chức năng chỉ ra loại cụ thể của một đối tượng thông qua 1) RTTI hoặc 2) một công tắc đơn giản. Nó làm như vậy 50.000.000 lần. Không để quảng cáo thêm, tôi trình bày cho bạn thời gian chạy tương đối cho 50.000.000 lượt chạy.
Đúng vậy, dynamicCasts
mất 94% thời gian chạy. Trong khi regularSwitch
khối chỉ chiếm 3,3% .
Câu chuyện dài ngắn: Nếu bạn có thể đủ năng lượng để kết nối một enum
loại như tôi đã làm dưới đây, có lẽ tôi khuyên bạn nên làm điều đó, nếu bạn cần làm RTTI và hiệu suất là tối quan trọng. Chỉ cần thiết lập thành viên một lần (đảm bảo có được thông qua tất cả các hàm tạo ) và đảm bảo không bao giờ viết nó sau đó.
Điều đó nói rằng, làm điều này không nên làm rối loạn các thực hành OOP của bạn .. nó chỉ được sử dụng khi thông tin loại đơn giản là không có sẵn và bạn thấy mình bị dồn vào sử dụng RTTI.
#include <stdio.h>
#include <vector>
using namespace std;
enum AnimalClassTypeTag
{
TypeAnimal=1,
TypeCat=1<<2,TypeBigCat=1<<3,TypeDog=1<<4
} ;
struct Animal
{
int typeTag ;// really AnimalClassTypeTag, but it will complain at the |= if
// at the |='s if not int
Animal() {
typeTag=TypeAnimal; // start just base Animal.
// subclass ctors will |= in other types
}
virtual ~Animal(){}//make it polymorphic too
} ;
struct Cat : public Animal
{
Cat(){
typeTag|=TypeCat; //bitwise OR in the type
}
} ;
struct BigCat : public Cat
{
BigCat(){
typeTag|=TypeBigCat;
}
} ;
struct Dog : public Animal
{
Dog(){
typeTag|=TypeDog;
}
} ;
typedef unsigned long long ULONGLONG;
void dynamicCasts(vector<Animal*> &zoo, ULONGLONG tests)
{
ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
for( ULONGLONG i = 0 ; i < tests ; i++ )
{
for( Animal* an : zoo )
{
if( dynamic_cast<Dog*>( an ) )
dogs++;
else if( dynamic_cast<BigCat*>( an ) )
bigcats++;
else if( dynamic_cast<Cat*>( an ) )
cats++;
else //if( dynamic_cast<Animal*>( an ) )
animals++;
}
}
printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;
}
//*NOTE: I changed from switch to if/else if chain
void regularSwitch(vector<Animal*> &zoo, ULONGLONG tests)
{
ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
for( ULONGLONG i = 0 ; i < tests ; i++ )
{
for( Animal* an : zoo )
{
if( an->typeTag & TypeDog )
dogs++;
else if( an->typeTag & TypeBigCat )
bigcats++;
else if( an->typeTag & TypeCat )
cats++;
else
animals++;
}
}
printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;
}
int main(int argc, const char * argv[])
{
vector<Animal*> zoo ;
zoo.push_back( new Animal ) ;
zoo.push_back( new Cat ) ;
zoo.push_back( new BigCat ) ;
zoo.push_back( new Dog ) ;
ULONGLONG tests=50000000;
dynamicCasts( zoo, tests ) ;
regularSwitch( zoo, tests ) ;
}