Является ли данное руководство девиртуализацией законным / жизнеспособным? - PullRequest
0 голосов
/ 06 ноября 2019

Для моего проекта мне, наконец, нужно поработать с моим первым полиморфным классом (кроме std :: cout).

Я исследую, как быть уверенным, что у меня 100% -ные девиртуализированные вызовы, по крайней мере, в некоторыхслучаи.

Является ли этот код законным и жизнеспособным?

Насколько медленным будет dynamic_cast? Если я заплачу один раз в конструкторе, я всегда смогу использовать класс B напрямую, так что это звучит как хорошая практика?

Будет ли приведение типа C быстро после этого или я должен сохранить указатель наB?

struct A{
    virtual void x();
};

struct B : A{
    void x() override;
};

struct X{
    A *a;
    bool fB;

    X(A &a) : a(&a), fB( dynamic_cast<B *>(&a) ){}

    void f(){
        if (fB){
            ((B*)a)->x();
        }else{
            a->x();
        }
    }
};


void fn(A &a){
    X x(a);

    x.f();
}


int main(){
    B b;

    X x(b);

    x.f();
}

Ответы [ 2 ]

1 голос
/ 06 ноября 2019

С вашим существующим кодом, я бы на самом деле надеялся, что ваш компилятор оптимизирует оператор if и ваше приведение, поскольку это приводит к пессимизации кода.

С точки зрения компилятора, ваша база кода может выглядетьследующим образом:

struct A{
    virtual void x();
};

struct B : A{
    void x() override;
};

struct C : B{
    void x() override;
};

Итак, когда is видит ваше приведение и вызов функции: static_cast<B*>(a)->x() он все равно должен обращаться к той же виртуальной таблице, что и при вызове a->x(), так как может существовать потенциальный классC. (Обратите внимание, что я использую static_cast, поскольку приведения в стиле c являются источниками ошибок)

Если вы хотите получить прямой вызов B, вам лучше создать метод или класс final. Другим хорошим подходом является использование оптимизации на основе профилей, в этом случае они часто сравнивают указатели vtable.

Чтобы ответить на ваши дополнительные вопросы?

  • Является ли код законным? Да, это так.
  • Это жизнеспособно? Да, учитывая замечания выше.
  • Насколько медленным будет dynamic_cast? Медленно, я бы посоветовал написать хороший тест для этого, однако я не знал бы, как это сделать и сделать его реалистичным.
  • Это хорошая практика? Нет, это делает полиморфизм менее пригодным для использования.
  • Будет ли static_cast быстрым? Да, это быстро, в данном конкретном случае никаких инструкций не требуется. Хранение указателя на B может улучшиться в определенных случаях сложного наследования по сравнению с bool. Если для этого потребуется дополнительная память, это также может привести к снижению производительности. Еще одно уменьшение может быть хорошо, если просто добавить дополнительную сборку в ваш исполняемый файл.

Мой совет, особенно если вы новичок в C ++: не делайте этого или каких-либо других ручных оптимизаций. Компиляторы могут многое сделать для вас. Каждая ручная оптимизация впоследствии требует дополнительного обслуживания, используйте эти приемы только в том случае, если у вас действительно есть проблема с производительностью.

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

1 голос
/ 06 ноября 2019

Вы пытаетесь изменить виртуальный вызов на филиал.

Я уже не уверен, что это было быстрее,

, но, что еще хуже, вы не удаляете виртуальный вызов, поскольку B можетЕсли у вас есть subchild, вам, по крайней мере, нужно сделать void B::x() final, чтобы компилятор мог девиртуализировать вызов.

Если у компилятора есть доступ к конкретному типу (и к коду), как в main, он можетдевиртуализировать в одиночку вызов.

...