Несколько уровней базовых классов замедляют класс/структуру в C++?

Вы можете использовать unname для этого:

> d <- do.call(rbind, unname(l1))
> d[10:15, ]
               mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Merc 230      22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
Ferrari Dino  19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
Merc 240D     24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
Mazda RX4     21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Merc 280      19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
14
задан Brian R. Bondy 19 September 2008 в 04:01
поделиться

12 ответов

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

Редактирование: Как отмечено в другом месте, в некоторых сценариях множественного наследования, корректировка 'этого' указателя требуется прежде, чем выполнить вызов. Raymond Chen описывает , как это работает на COM-объекты. В основном вызывание виртуальной функции на объекте, который наследовался нескольким основаниям, может потребовать дополнительного вычитания и jmp инструкции сверх дополнительного поиска указателя, требуемого для виртуального вызова.

25
ответ дан 1 December 2019 в 06:49
поделиться

Как указано Corey Ross vtable известно во время компиляции любым листовым производным классом, и таким образом, стоимость виртуального вызова действительно должна быть тем же независимо от структуры иерархии.

Это, однако, не может быть сказано для dynamic_cast. Если Вы рассмотрите, как Вы могли бы реализовать dynamic_cast, основной подход будет, имеют O (n), перерывают Вашу иерархию!

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

sturct A { int i; };
struct B { int j; };

struct C : public A, public B { int k ; };

// Let's assume that the layout of C is:  { [ int i ] [ int j ] [int k ] }

void foo (C * c) {
  A * a = c;                // Probably has zero cost
  B * b = c;                // Compiler needed to add sizeof(A) to 'c'
  c = static_cast<B*> (b);  // Compiler needed to take sizeof(A)' from 'b'
}
0
ответ дан 1 December 2019 в 06:49
поделиться

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

Однако, если Вы звоните до функций в базовый класс, которые вызывают функции в его базовом классе, и так далее, много, он мог повлиять на производительность.

В сущности, это высоко зависит от того, как Вы структурировали свое дерево наследования.

-1
ответ дан 1 December 2019 в 06:49
поделиться

[Глубокие иерархии наследования] значительно увеличивают нагрузку обслуживания, добавляя ненужную сложность, вынуждая пользователей изучить интерфейсы многих классов, даже когда все, что они хотят сделать, использовать определенный производный класс. Это может также оказать влияние на использование памяти и производительность программы путем добавления ненужного vtables и косвенности к классам, которым действительно не нужны они. При нахождении часто глубоких иерархий наследования необходимо рассмотреть стиль дизайна, чтобы видеть, переняли ли Вы эту дурную привычку. Глубокие иерархии редко необходимы и почти никогда не хорошие. И если Вы не полагаете, что, но думают, что "OO просто не является OO без большого наследования", затем хороший контрпример для рассмотрения является [C++] самой библиотекой стандарта. - Herb Sutter

6
ответ дан 1 December 2019 в 06:49
поделиться

Нет никакого различия в скорости между виртуальными вызовами на разных уровнях, так как они все выровнены в vtable (указывающий на наиболее полученные версии переопределенных методов). Так, звоня ((*) inst)-> Метод (), когда inst является экземпляром B, является теми же издержками как тогда, когда inst является экземпляром D.

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

2
ответ дан 1 December 2019 в 06:49
поделиться
  • множественное наследование замедляет класс?

, Как упомянуто несколько раз, глубоко вложенная иерархия единичного наследования не должна налагать дополнительные издержки для виртуального вызова (выше издержек, наложенных ни для какого виртуального вызова).

Однако, когда множественное наследование включено, иногда существуют очень небольшие дополнительные издержки при вызывании виртуальной функции через указатель базового класса. В этом случае некоторые реализации имеют виртуальную функцию, проходят маленького преобразователя, который корректирует 'этот' указатель, так как

(static_cast<Base*>( this) == this)

не обязательно верно в зависимости от структуры объекта.

Примечание, что все это очень , очень зависящее от реализации.

Посмотрите Lippman "В Главе 4.2" Модели Объекта C++ - Виртуальные членские Функции/Виртуальные функции под MI

4
ответ дан 1 December 2019 в 06:49
поделиться

Почти все ответы указывают на то, были ли виртуальные методы медленнее в примере OP, но я думаю, что OP просто спрашивает, если наличие нескольких уровней наследования в и себя является медленным. Ответ, конечно, не, так как это все происходит во время компиляции в C++. Я подозреваю, что вопрос управляется на основе опыта с языками сценария, где такие графики наследования могут быть динамичными, и в этом случае, это потенциально могло быть медленнее.

2
ответ дан 1 December 2019 в 06:49
поделиться

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

Brian, чтобы быть ясным и ответить на Ваш комментарий. Если нет никаких виртуальных функций нигде в Вашем дереве наследования, то нет никакого влияния производительности.

1
ответ дан 1 December 2019 в 06:49
поделиться

Сами виртуальные вызовы являются более трудоемкими, чем обычные вызовы, потому что это имеет к поиску адрес фактической функции для вызова от vtable

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

Вот надлежащее исследование, которое говорит, что издержки могут составить целых 50% , http://www.cs.ucsb.edu/~urs/oocsb/papers/oopsla96.pdf

Вот является другим ресурсом, который смотрит на побочный эффект наличия крупной библиотеки виртуальных классов http://keycorner.org/pub/text/doc/kde-slow.txt

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

Относительно Вашего конкретного вопроса наличия большого никакие из базовых классов, обычно расположение памяти объекта класса имело бы vtbl ptrs для всех других составляющих классов в нем.

Проверка эта страница для демонстрационного vtable расположения - http://www.codesourcery.com/public/cxx-abi/cxx-vtable-ex.html

Так вызов к методу, реализованному классом глубже в heierarchy, все еще только был бы единственной косвенностью и не несколькими косвенностью, поскольку Вы, кажется, думаете. Вызов не должен перейти от класса до класса, чтобы наконец найти, что точная функция звонит.

Однако при использовании состава вместо наследования, каждый вызов указателя был бы виртуальным вызовом и который наверху присутствовал бы и если в рамках того виртуального вызова, если бы тот класс использует больше compositio, n больше виртуальных вызовов, был бы сделан. Это kindof, дизайн был бы медленнее в зависимости от суммы вызовов, которые Вы выполнили.

2
ответ дан 1 December 2019 в 06:49
поделиться

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

1
ответ дан 1 December 2019 в 06:49
поделиться

Вызывание виртуальной функции немного медленнее, чем вызывание невиртуальной функции. Однако я не думаю, что имеет значение, как глубоко Ваше дерево наследования.

, Но это не различие, по поводу которого Вы должны обычно волноваться.

0
ответ дан 1 December 2019 в 06:49
поделиться

Да, если Вы ссылаетесь на него как это:

// F is-a E,
// E is-a D and so on

A* aObject = new F(); 
aObject->CallAVirtual();

Затем Вы работаете с указателем на текстовый объект. Учитывая Вы вызываете функцию, которая является виртуальной, она должна искать функциональную таблицу (vtable) для получения корректных указателей. Существуют немного служебные к этому, да.

0
ответ дан 1 December 2019 в 06:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: