runtime — Эффективная проверка типов во время выполнения в переполнении стека

У меня есть два класса с именем «Выражение» и «BinExp» как следующие коды:

class Expression
{
public:
virtual BinExp* IsBinaryExp() { return NULL; }
};
class BinExp : public Expression
{
public:
virtual BinExp* IsBinaryExp() { return this; }
};

Например, у меня есть переменная указателя типа Expression* но инициализируется как new BinExp и отправить в качестве аргумента analyse функционировать как следующий код:

int main()
{
Expression* e = new BinExp;
analyse(e);
}

Внутри analyse функция, мне нужно знать, является ли e это указатель на Expression тип или BinExp тип. В моей руке есть три способа сделать это.

Первый:

BinExp* be = e->IsBinaryExp();
if ( be )
{
printf("Yes, `e` is a binary expression\n");
}

Во-вторых:

BinExp* be = dynamic_cast<BinExp*>(e);
if ( be )
{
printf("Yes, `e` is a binary expression\n");
}

И третье:

if ( typeid(*e) == typeid(BinExp) )
{
BinExp* be = e->IsBinaryExp(); // or dynamic_cast<BinExp*>(e);
printf("Yes, `e` is a binary expression\n");
}

Но я хочу знать, какой из этих способов (или любой другой) будет более эффективным и действенным, когда мне нужно будет часто выполнять проверку внутри цикла, где важна производительность. Любое предложение я буду признателен.

0

Решение

Самый быстрый способ — сохранить переменную-член, скажем, enum, затем определить в базовом классе встроенный метод получения, тогда вы сможете сравнить, соответствует ли результат ожидаемому.

Пример (не скомпилированный, возможны некоторые ошибки):

  enum eExpTypes {
ET_UNDEFINED,
ET_BINARY
}
class Expresion
{
protected:
eExpTypes myType;
public:
Expresion(): myType(ET_UNDEFINED){};
inline eExpTypes getType(){return myType;};
}

class BinExpresion : public Expresion
{
public:
BinExpresion():myType(ET_BINARY){};
}

Прирост производительности:

  • Вы вытащите два указателя: от указателя на vfptable, от vfptable до функции
  • ваш размер класса будет меньше, если функция типа является единственной виртуальной функцией

Динамическое приведение обычно медленнее, чем создание собственного механизма проверки типа, поэтому в случае ваших трех примеров первый должен быть самым быстрым.

2

Другие решения

Самый быстрый будет:

e->printIsBinExp();

Где вы делаете тот виртуальный метод, который либо печатает, либо является пустяком.

Я только частично шучу. Точка virtual Методы заключаются в том, чтобы инкапсулировать обработку различных типов конкретного метода, а не писать программу, которая просто переключает среду выполнения на то, какими могут быть различные типы среды выполнения.

Предположим, dynamic_cast был самым быстрым Не могли бы вы написать:

if (BinExp* be = dynamic_cast<BinExp*>(e)) {
// ...
}
else if (UnExp* ue = dynamic_cast<UnExp*>(e)) {
// ...
}
else if (TernExp* te = dynamic_cast<TernExp*>(e)) {
// ...
}

Надеюсь, что нет. Этот код будет очень хрупким. Вы определенно захотите придумать дизайн, который бы:

e->eval();

просто делает правильную вещь как один virtual вызов.

0

Номер 3 самый элегантный.

Чтобы выяснить, является ли или какой из них наиболее эффективным, можно использовать некий простой код для измерения времени выполнения каждого случая …

#include <iostream>
#include <chrono>

/*
Case
*/

int main()
{
const clock_t begin_time = clock();
// Case
std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC;

system("pause");
return 0;
}
-1
По вопросам рекламы [email protected]