Мне нужен был полиморфизм во время выполнения, поэтому я использовал dynamic_cast
.
Но теперь у меня были две проблемы: dynamic_cast
был крайне медленным! (Прокрутите вниз для эталона.)
Короче говоря, я решил проблему таким образом, используя static_cast
:
struct Base
{
virtual ~Base() { }
virtual int type_id() const = 0;
template<class T>
T *as()
{ return this->type_id() == T::ID ? static_cast<T *>(this) : 0; }
template<class T>
T const *as() const
{ return this->type_id() == T::ID ? static_cast<T const *>(this) : 0; }
};
struct Derived : public Base
{
enum { ID = __COUNTER__ }; // warning: can cause ABI incompatibility
int type_id() const { return ID; }
};
int main()
{
Base const &value = Derived();
Derived const *p = value.as<Derived>(); // "static" dynamic_cast
}
Но я, конечно, не первый, кто столкнулся с этой проблемой, поэтому я подумал, что, возможно, стоит спросить:
Вместо того, чтобы придумывать домашнее решение, подобное этому, есть ли хорошо известный шаблон/библиотека, который я могу использовать для решения этой проблемы в будущем?
Образец бенчмарка
Чтобы понять, о чем я говорю, попробуйте приведенный ниже код: dynamic_cast
был примерно в 15 раз медленнее, чем простой вызов virtual
на моем компьютере (110 мс против 1620 мс с кодом ниже):
#include <cstdio>
#include <ctime>
struct Base { virtual unsigned vcalc(unsigned i) const { return i * i + 1; } };
struct Derived1 : public Base
{ unsigned vcalc(unsigned i) const { return i * i + 2; } };
struct Derived2 : public Derived1
{ unsigned vcalc(unsigned i) const { return i * i + 3; } };
int main()
{
Base const &foo = Derived2();
size_t const COUNT = 50000000;
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = foo.vcalc(n);
printf("virtual call: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = dynamic_cast<Derived1 const &>(foo).vcalc(n);
printf("virtual call after dynamic_cast: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
return 0;
}
Когда я просто удаляю слово virtual
и заменяю dynamic_cast
на static_cast
, я получаю время выполнения 79 мс, поэтому виртуальный вызов медленнее статического вызова только примерно на 25%!
dynamic_cast
? В большинстве случаев вы можете полностью отказаться от приведения типов, улучшив дизайн, но то, как это будет сделано, зависит от конкретной проблемы, которую вы пытаетесь решить. - person casablanca   schedule 10.09.2012dynamic_cast
так много раз, что это действительно влияет на производительность, вам, возможно, придется пересмотреть свой дизайн. - person casablanca   schedule 10.09.2012dynamic_cast
? Не использовать его. Полиморфизм времени выполнения иdynamic_cast
— почти противоположные понятия: полиморфизм — это возможность получить ссылку на объект и использовать его, не заботясь о типе,dynamic_cast
— это забота о типе, потому что вы не можете полиморфно добраться до функции. Лучший совет уже дала @casablanca: пересмотрите дизайн. Если вы используетеdynamic_cast
, цель состоит не в том, чтобы сделать его быстрее, а в том, чтобы удалить его из дизайна. - person David Rodríguez - dribeas   schedule 10.09.2012isa
,dyn_cast
и т. д., которые накладывают ограничения на иерархию классов и выполняют некоторую ручную работу для каждого класса. , но смехотворно эффективен (просто загружает элемент и сравнивает его с постоянным IIRC). Это может быть излишним, или ограничения могут быть слишком жесткими, но тем не менее это довольно интересно. - person   schedule 10.09.2012