Накладные расходы на создание нового класса - PullRequest
19 голосов
/ 04 декабря 2010

Если у меня есть класс, определенный так:

class classWithInt
{
public:
    classWithInt();
...
private:
    int someInt;
...
}

и что someInt является единственной и единственной переменной-членом в classWithInt, насколько медленнее будет объявление нового экземпляраэтого класса, чем просто объявить новое целое число?

Как насчет того, когда у вас есть, скажем, 10 таких целых чисел в классе?100

Ответы [ 2 ]

41 голосов
/ 04 декабря 2010

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

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

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

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

7 голосов
/ 04 декабря 2010

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

void use(int &);

class classWithInt
{
public:
    classWithInt() : someInt(){}
    int someInt;
};
class podWithInt
{
public:
    int someInt;
};

int main() {
  int foo;
  classWithInt bar;
  podWithInt baz;

  use(foo);
  use(bar.someInt);
  use(baz.someInt);

  return 5;

}

И это вывод, который я получаю из gcc-llvm

; ModuleID = '/tmp/webcompile/_21792_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"

%struct.classWithInt = type { i32 }

define i32 @main() {
entry:
  %foo = alloca i32, align 4                      ; <i32*> [#uses=1]
  %bar = alloca %struct.classWithInt, align 8     ; <%struct.classWithInt*> [#uses=1]
  %baz = alloca %struct.classWithInt, align 8     ; <%struct.classWithInt*> [#uses=1]
  %0 = getelementptr inbounds %struct.classWithInt* %bar, i64 0, i32 0 ; <i32*> [#uses=2]
  store i32 0, i32* %0, align 8
  call void @_Z3useRi(i32* %foo)
  call void @_Z3useRi(i32* %0)
  %1 = getelementptr inbounds %struct.classWithInt* %baz, i64 0, i32 0 ; <i32*> [#uses=1]
  call void @_Z3useRi(i32* %1)
  ret i32 5
}

declare void @_Z3useRi(i32*)

В каждом случае есть некоторые различия. В простейшем случае тип POD отличается от простого int только одним способом, он требует другого выравнивания, он выровнен на 8 байтов вместо всего 4 байтов.

Другая заметная вещь заключается в том, что POD и голое int не инициализируются. Их хранилище используется прямо из пустыни стека. Тип не-pod, имеющий нетривиальный конструктор, заставляет сохранять ноль, прежде чем экземпляр можно будет использовать для чего-либо еще.

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